Sistema de Ventas · Venezuela
Tasa BCV Oficial en Tiempo Real
Consultando BCV...
conectando...
Selecciona tu perfil
VERSIÓN NUEVA v5 — cargando...
Bienvenido/a
Ingresa tu código de acceso
🔒 Bloqueado · 0s
MUSTAPAN
$1=...Bs
`); pw.document.close();pw.focus();setTimeout(()=>pw.print(),400); }catch(e){} } function reimprR(id){const v=E.ventas.find(x=>idEq(x.id,id));if(v) mostrarR(v);} // ══════════════════════════════════════ // PEDIDOS // ══════════════════════════════════════ const PEST={pendiente:{lb:'Pendiente',cl:'bwn2',next:'listo',nlb:'✅ Listo'},listo:{lb:'Listo',cl:'bok2',next:'entregado',nlb:'📦 Entregado'},entregado:{lb:'Entregado',cl:'binf',next:null},cancelado:{lb:'Cancelado',cl:'berr2',next:null}}; function rPeds(){ const pend=E.peds.filter(p=>p.est==='pendiente'); const list=E.peds.filter(p=>p.est==='listo'); const hist=E.peds.filter(p=>p.est==='entregado'||p.est==='cancelado').slice(-6); const pH=p=>{const e=PEST[p.est]||PEST.pendiente;const r=p.tot-(p.adel||0); return `
${san(p.cli)}${p.tel?` · ${san(p.tel)}`:''}
📝 #${p.id} · ${p.hora||'sin hora'} · ${new Date(p.ts).toLocaleDateString('es-VE')}
${e.lb}
${san(p.desc)}
${p.nota?`
📌 ${san(p.nota)}
`:''}
Total: ${fU(p.tot)} ${p.adel?` · Adelanto: ${fU(p.adel)} · Resta: ${fU(r)}`:''}
${e.next?``:''} ${p.est!=='entregado'&&p.est!=='cancelado'?``:''}
`;}; return `
📝 Pedidos
${pend.length?`
⏳ Pendientes (${pend.length})
${pend.map(pH).join('')}`:''} ${list.length?`
✅ Listos (${list.length})
${list.map(pH).join('')}`:''} ${!pend.length&&!list.length?`
📝
Sin pedidos activos
`:''} ${hist.length?`
Historial reciente
${hist.map(pH).join('')}`:''}`; } function abrPed(){['ped-cli','ped-desc','ped-nota','ped-tel'].forEach(id=>document.getElementById(id).value='');document.getElementById('ped-tot').value='';document.getElementById('ped-adel').value='0';document.getElementById('ped-hora').value='';aM('mo-ped');} function savePed(){ try{ const cli=document.getElementById('ped-cli').value.trim(),desc=document.getElementById('ped-desc').value.trim(); const tot=safeN(document.getElementById('ped-tot').value,0,0); const adel=safeN(document.getElementById('ped-adel').value,0,0); const hora=document.getElementById('ped-hora').value,nota=document.getElementById('ped-nota').value.trim(),tel=document.getElementById('ped-tel').value.trim(); if(!cli){notif('⚠️ Nombre del cliente requerido');return;} if(!desc){notif('⚠️ Describe el pedido');return;} if(!tot){notif('⚠️ Ingresa el total');return;} E.peds.push({id:E.nPed++,cli:san(cli),desc:san(desc),tot,adel,hora,nota:san(nota),tel:san(tel),est:'pendiente',eId:E.ses.id,eNom:E.ses.nom,ts:new Date().toISOString()}); logAc('pedido',`${san(cli)} ${fU(tot)}`);saveData();cM('mo-ped');notif('✅ Pedido registrado');renderTab(); }catch(e){notif('❌ Error al guardar pedido');} } function avPed(id){const p=E.peds.find(x=>idEq(x.id,id));if(!p)return;const e=PEST[p.est];if(e.next){p.est=e.next;saveData();notif(`✅ ${PEST[p.est].lb}`);renderTab();}} function canPed(id){if(!confirm('¿Cancelar este pedido?'))return;const p=E.peds.find(x=>idEq(x.id,id));if(p){p.est='cancelado';saveData();notif('✖ Cancelado');renderTab();}} // ══════════════════════════════════════ // GASTOS // ══════════════════════════════════════ function rGastos(){ const gh=gHoy(),totG=gh.reduce((s,g)=>s+g.monto,0),totV=vHoy().reduce((s,v)=>s+v.tot,0),net=totV-totG; const pC={};gh.forEach(g=>pC[g.cat]=(pC[g.cat]||0)+g.monto); return `
💸 Gastos del Día
💸 Gastos
${fU(totG)}
${fB(toB(totG))}
📈 Neta
${fU(net)}
${fB(toB(net))}
${Object.keys(pC).length?`
Por categoría
${Object.entries(pC).map(([k,v])=>`
${GCAT[k]||k}${fU(v)}
`).join('')}
`:''} ${gh.length?`
Gastos de Hoy
${gh.slice().reverse().map(g=>`
${(GCAT[g.cat]||'📦').charAt(0)}
${san(g.desc)}
${GCAT[g.cat]||g.cat} · ${g.eNom}
${fU(g.monto)}
${fB(toB(g.monto))}
${pue('del')?``:''}
`).join('')}
`: `
💸
Sin gastos hoy
`}`; } function prevG(){try{const v=safeN(document.getElementById('g-monto').value,0,0);const el=document.getElementById('g-bs');if(el) el.textContent=v>0?`En Bs: ${fB(toB(v))}`:''; }catch(e){}} function saveGasto(){ try{ const desc=document.getElementById('g-desc').value.trim(),monto=safeN(document.getElementById('g-monto').value,0,0.01),cat=document.getElementById('g-cat').value; if(!desc){notif('⚠️ Describe el gasto');return;}if(monto<=0){notif('⚠️ Monto inválido');return;} E.gastos.push({id:E.nGasto++,desc:san(desc),monto,cat,eId:E.ses.id,eNom:E.ses.nom,ts:new Date().toISOString()}); logAc('gasto',`${san(desc)} ${fU(monto)}`);saveData();cM('mo-gasto');notif(`✅ Gasto: -${fU(monto)}`); document.getElementById('g-desc').value='';document.getElementById('g-monto').value='';document.getElementById('g-bs').textContent=''; renderTab(); }catch(e){notif('❌ Error al guardar gasto');} } function delG(id){if(!confirm('¿Eliminar este gasto?'))return;E.gastos=E.gastos.filter(x=>!idEq(x.id,id));saveData();notif('🗑️ Eliminado');renderTab();} // ══════════════════════════════════════ // FIADO // ══════════════════════════════════════ function rFiado(){ if(!pue('fiado')) return noP(); const pend=E.fiados.filter(f=>f.est!=='pagado'),pags=E.fiados.filter(f=>f.est==='pagado').slice(-6); const totP=pend.reduce((s,f)=>s+(f.tot-f.pag),0); const fH=f=>{const r=f.tot-f.pag,es=r<=0?'pagado':f.pag>0?'parcial':'pendiente';const bc=es==='pagado'?'bok2':es==='parcial'?'bwn2':'berr2'; return `
👤 ${san(f.cli)}
${new Date(f.ts).toLocaleDateString('es-VE')} · ${f.eNom}${f.desc?' · '+san(f.desc):''}
${es}
Total: ${fU(f.tot)}${f.pag?` · Cobrado: ${fU(f.pag)}`:''}
${r>0?`
Por cobrar: ${fU(r)}
`:''}
${r>0?``:''}
`;}; return `
🤝 Crédito (Fiado)
${pend.length?`
TOTAL POR COBRAR
${fU(totP)}
${pend.length} fiados · ${fB(toB(totP))}
`:''} ${pend.length?`
🔴 Pendientes
${pend.map(fH).join('')}`:`
🤝
¡Sin deudas!
`} ${pags.length?`
✅ Cobrados
${pags.map(fH).join('')}`:''}`; } function saveFiado(){ try{ const cli=document.getElementById('f-cli').value.trim(),desc=document.getElementById('f-desc').value.trim(),tot=safeN(document.getElementById('f-tot').value,0,0.01),adel=safeN(document.getElementById('f-adel').value,0,0); if(!cli){notif('⚠️ Nombre requerido');return;}if(!tot){notif('⚠️ Ingresa el total');return;} E.fiados.push({id:E.nFiado++,cli:san(cli),desc:san(desc),tot,pag:adel,pagos:adel>0?[{monto:adel,met:'adelanto',ts:new Date().toISOString()}]:[],est:adel>=tot?'pagado':'pendiente',eId:E.ses.id,eNom:E.ses.nom,ts:new Date().toISOString()}); logAc('fiado',`${san(cli)} ${fU(tot)}`);saveData();cM('mo-fiado');notif('✅ Fiado registrado'); ['f-cli','f-desc','f-tot'].forEach(id=>document.getElementById(id).value='');document.getElementById('f-adel').value='0'; renderTab(); }catch(e){notif('❌ Error al guardar');} } let cobroId=null; function abrCobro(id){ cobroId=id;const f=E.fiados.find(x=>idEq(x.id,id));if(!f)return;const r=f.tot-f.pag; document.getElementById('cobro-info').innerHTML=''+san(f.cli)+'
Total: '+fU(f.tot)+' · Cobrado: '+fU(f.pag)+'
Por cobrar: '+fU(r)+''; document.getElementById('cobro-monto').value=r.toFixed(2);aM('mo-cobro'); } function saveCobro(){ try{ const f=E.fiados.find(x=>idEq(x.id,cobroId));if(!f)return; const m=safeN(document.getElementById('cobro-monto').value,0,0.01),met=document.getElementById('cobro-met').value; if(!m){notif('⚠️ Monto inválido');return;} f.pag=Math.min(f.tot,f.pag+m); if(!f.pagos) f.pagos=[];f.pagos.push({monto:m,met,ts:new Date().toISOString()}); f.est=f.pag>=f.tot?'pagado':'parcial'; logAc('cobro',`${san(f.cli)} ${fU(m)}`);saveData();cM('mo-cobro');notif(`✅ Pago de ${fU(m)} registrado`);renderTab(); }catch(e){} } // ══════════════════════════════════════ // CLIENTES // ══════════════════════════════════════ function rClis(){ if(!pue('clis')) return noP(); return `
👥 Clientes
${!E.clis.length?`
👥
Sin clientes
`: E.clis.map(c=>{const compras=E.ventas.filter(v=>v.cliId===c.id),tot=compras.reduce((s,v)=>s+v.tot,0);return `
${c.nom.charAt(0)}
${san(c.nom)}
${c.tel||'Sin teléfono'}${c.rif?' · '+san(c.rif):''}
${c.notas?`
📌 ${san(c.notas)}
`:''}
${fU(tot)}
${compras.length} compras
`;}).join('')}`; } function abrCli(){E.editCId=null;document.getElementById('mcli-t').textContent='👤 Nuevo Cliente';['c-n','c-t','c-r','c-nota'].forEach(id=>document.getElementById(id).value='');aM('mo-cli');} function editCli(id){const c=E.clis.find(x=>idEq(x.id,id));if(!c)return;E.editCId=id;document.getElementById('mcli-t').textContent='✏️ Editar';document.getElementById('c-n').value=c.nom;document.getElementById('c-t').value=c.tel||'';document.getElementById('c-r').value=c.rif||'';document.getElementById('c-nota').value=c.notas||'';aM('mo-cli');} function saveCli(){ try{ const n=document.getElementById('c-n').value.trim(),t=document.getElementById('c-t').value.trim(),r=document.getElementById('c-r').value.trim(),nota=document.getElementById('c-nota').value.trim(); if(!n){notif('⚠️ Nombre requerido');return;} if(E.editCId){const idx=E.clis.findIndex(x=>idEq(x.id,E.editCId));if(idx>=0) E.clis[idx]={...E.clis[idx],nom:san(n),tel:san(t),rif:san(r),notas:san(nota)};notif('✅ Cliente actualizado');} else{E.clis.push({id:E.nCli++,nom:san(n),tel:san(t),rif:san(r),notas:san(nota),ts:new Date().toISOString()});notif('✅ Cliente guardado');} saveData();cM('mo-cli');renderTab(); }catch(e){notif('❌ Error');} } // ══════════════════════════════════════ // REPORTES // ══════════════════════════════════════ function rReportes(){ if(!pue('rep')) return noP(); const hoy=new Date(); const dias7=Array.from({length:7},(_,i)=>{const d=new Date(hoy);d.setDate(d.getDate()-(6-i));return d;}); const lbls=dias7.map(d=>['Dom','Lun','Mar','Mié','Jue','Vie','Sáb'][d.getDay()]); const data7=dias7.map(d=>Math.round(E.ventas.filter(v=>new Date(v.ts).toDateString()===d.toDateString()).reduce((s,v)=>s+v.tot,0)*100)/100); const gas7=dias7.map(d=>Math.round(E.gastos.filter(g=>new Date(g.ts).toDateString()===d.toDateString()).reduce((s,g)=>s+g.monto,0)*100)/100); const mets={efectivo_usd:0,efectivo_bs:0,pago_movil:0,zelle:0,binance:0}; const mesAct=E.ventas.filter(v=>{const d=new Date(v.ts);return d.getMonth()===hoy.getMonth()&&d.getFullYear()===hoy.getFullYear();}); mesAct.forEach(v=>mets[v.met]=(mets[v.met]||0)+v.tot); const totMes=mesAct.reduce((s,v)=>s+v.tot,0); const pC={};E.ventas.forEach(v=>v.items.forEach(i=>pC[i.nom]=(pC[i.nom]||0)+i.qty)); const top5=Object.entries(pC).sort((a,b)=>b[1]-a[1]).slice(0,5); window._rd={lbls,data7,gas7,mets}; return `
📈 Reportes
📅 Este Mes
${fU(totMes)}
${mesAct.length} ventas
🧾 Total
${E.ventas.length}
ventas históricas
📊 Ventas y Gastos — 7 días
💳 Métodos
🏆 Más Vendidos
${top5.map(([n,q],i)=>`
${['🥇','🥈','🥉','4️⃣','5️⃣'][i]} ${n.length>13?n.slice(0,13)+'..':n}${q}
`).join('')} ${!top5.length?`
Sin datos
`:''}
📤 Exportar
`; } function initCharts(){ try{ if(typeof Chart==='undefined'){setTimeout(initCharts,400);return;} const d=window._rd||{lbls:[],data7:[],gas7:[],mets:{}}; const tm=TEMAS[E.cfg.tema||'dorado']; Object.values(_charts).forEach(c=>{try{c.destroy();}catch(e){}});_charts={}; const cb=document.getElementById('chart-bar'); if(cb) _charts.bar=new Chart(cb,{type:'bar',data:{labels:d.lbls,datasets:[{label:'Ventas $',data:d.data7,backgroundColor:tm.p+'CC',borderColor:tm.p,borderWidth:1},{label:'Gastos $',data:d.gas7,backgroundColor:'#B0202055',borderColor:'#B02020',borderWidth:1}]},options:{responsive:true,plugins:{legend:{labels:{font:{size:12}}}},scales:{y:{ticks:{callback:v=>'$'+v},grid:{color:'rgba(0,0,0,.05)'}},x:{grid:{display:false}}}}}); const cp=document.getElementById('chart-pie'); const pm=Object.values(d.mets); if(cp&&pm.some(v=>v>0)) _charts.pie=new Chart(cp,{type:'doughnut',data:{labels:['USD','Bs','PM','Zelle','Binance'],datasets:[{data:pm,backgroundColor:['#2E7D32','#C8A020','#1565C0','#880E4F','#F5A623'],borderWidth:1}]},options:{responsive:true,plugins:{legend:{position:'bottom',labels:{font:{size:11},padding:6}}}}}); }catch(e){} } function dlFile(name,content,type){try{const b=new Blob([content],{type});const u=URL.createObjectURL(b);const a=document.createElement('a');a.href=u;a.download=name;a.click();URL.revokeObjectURL(u);}catch(e){}} function importarDatos(){ try{ var input=document.createElement('input'); input.type='file'; input.accept='.json,application/json'; input.onchange=impBk; input.click(); }catch(e){notif('No se pudo abrir el importador');} } function expV(){ const h='ID,Fecha,Hora,Cajero,Total USD,Total Bs,Descuento,Metodo,Mesa,Nota,Productos\n'; const rows=E.ventas.map(v=>{const d=new Date(v.ts);return[v.id,d.toLocaleDateString('es-VE'),d.toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}),v.eNom,v.tot.toFixed(2),Math.round(v.totBs),(v.disc||0).toFixed(2),v.met,v.mesa||'',v.nota||'','"'+v.items.map(i=>i.qty+'x '+i.nom).join('; ')+'"'].join(',');}).join('\n'); dlFile(`mustapan_ventas_${new Date().toISOString().split('T')[0]}.csv`,h+rows,'text/csv'); } function expG(){ const h='ID,Fecha,Hora,Cajero,Descripcion,Categoria,Monto USD,Monto Bs\n'; const rows=E.gastos.map(g=>{const d=new Date(g.ts);return[g.id,d.toLocaleDateString('es-VE'),d.toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}),g.eNom,'"'+g.desc+'"',g.cat,g.monto.toFixed(2),Math.round(toB(g.monto))].join(',');}).join('\n'); dlFile(`mustapan_gastos_${new Date().toISOString().split('T')[0]}.csv`,h+rows,'text/csv'); } function expVXLSX(){ try{ if(typeof XLSX==='undefined'){notif('Cargando Excel...');return;} const wb=XLSX.utils.book_new(); const rows=[['ID','Fecha','Hora','Cajero','Total USD','Total Bs','Descuento','Metodo','Mesa','Nota','Productos']]; E.ventas.forEach(v=>{const d=new Date(v.ts);rows.push([v.id,d.toLocaleDateString('es-VE'),d.toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}),v.eNom,Number(v.tot||0),Math.round(v.totBs||0),Number(v.disc||0),METNOM[v.met]||v.met,v.mesa||'',v.nota||'',(v.items||[]).map(i=>i.qty+'x '+i.nom).join('; ')]);}); const ws=XLSX.utils.aoa_to_sheet(rows); ws['!cols']=[{wch:10},{wch:12},{wch:8},{wch:18},{wch:12},{wch:14},{wch:10},{wch:16},{wch:10},{wch:24},{wch:48}]; XLSX.utils.book_append_sheet(wb,ws,'Ventas'); XLSX.writeFile(wb,'mustapan_ventas_'+new Date().toISOString().split('T')[0]+'.xlsx'); notif('Excel de ventas descargado'); }catch(e){notif('Error Excel ventas: '+e.message);} } function expGXLSX(){ try{ if(typeof XLSX==='undefined'){notif('Cargando Excel...');return;} const wb=XLSX.utils.book_new(); const rows=[['ID','Fecha','Hora','Cajero','Descripcion','Categoria','Monto USD','Monto Bs']]; E.gastos.forEach(g=>{const d=new Date(g.ts);rows.push([g.id,d.toLocaleDateString('es-VE'),d.toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}),g.eNom,g.desc,GCAT[g.cat]||g.cat,Number(g.monto||0),Math.round(toB(g.monto||0))]);}); const ws=XLSX.utils.aoa_to_sheet(rows); ws['!cols']=[{wch:10},{wch:12},{wch:8},{wch:18},{wch:36},{wch:18},{wch:12},{wch:14}]; XLSX.utils.book_append_sheet(wb,ws,'Gastos'); XLSX.writeFile(wb,'mustapan_gastos_'+new Date().toISOString().split('T')[0]+'.xlsx'); notif('Excel de gastos descargado'); }catch(e){notif('Error Excel gastos: '+e.message);} } // ══════════════════════════════════════ // CIERRE // ══════════════════════════════════════ function rCierre(){ if(!pue('cierre')) return noP(); const vh=vHoy(),gh=gHoy(); const totV=vh.reduce((s,v)=>s+v.tot,0),totG=gh.reduce((s,g)=>s+g.monto,0),net=totV-totG; const mets={};vh.forEach(v=>mets[v.met]=(mets[v.met]||0)+v.tot); const gCatS={};gh.forEach(g=>gCatS[g.cat]=(gCatS[g.cat]||0)+g.monto); const pC2={};vh.forEach(v=>v.items.forEach(i=>pC2[i.nom]=(pC2[i.nom]||0)+i.qty)); const top=Object.entries(pC2).sort((a,b)=>b[1]-a[1]).slice(0,5); const pEmp={};if(pue('todos')){vh.forEach(v=>{if(!pEmp[v.eId]){pEmp[v.eId]={nom:v.eNom,tot:0,cnt:0};}pEmp[v.eId].tot+=v.tot;pEmp[v.eId].cnt++;});} const f=new Date().toLocaleDateString('es-VE',{weekday:'long',year:'numeric',month:'long',day:'numeric'}); const tasaUsada=TD.bcv>0?TD.bcv:E.cfg.tasaBCV; return `
📊 Cierre del Día
${f}
${pue('expfiles')?`
📥 Descargar Cierre
`:''}
VENTAS DEL DÍA
${fU(totV)}
${fB(toB(totV))}
Número de ventas:${vh.length}
Tasa BCV:${tasaUsada} Bs/$
${totG>0?`
GASTOS DEL DÍA
${fU(totG)}
${Object.entries(gCatS).map(([k,v])=>`
${GCAT[k]||k}-${fU(v)}
`).join('')}
`:''}
GANANCIA NETA
${fU(net)}
${fB(toB(net))}
${Object.keys(mets).length?`
💳 Por Método de Pago
${Object.entries(mets).map(([m,t])=>`
${METBL[m]||m}${fU(t)}
`).join('')}
`:''} ${top.length?`
🏆 Más Vendidos
${top.map(([n,q],i)=>`
${['🥇','🥈','🥉','4️⃣','5️⃣'][i]} ${san(n)}${q} ud.
`).join('')}
`:''} ${Object.keys(pEmp).length?`
👥 Por Cajero
${Object.values(pEmp).map(e=>`
${san(e.nom)}${fU(e.tot)} (${e.cnt})
`).join('')}
`:''} ${!vh.length?`
📊
Sin ventas hoy
`:''} ${pue('del')?`
⚠️ Solo SuperAdmin

Elimina permanentemente todo el historial.

`:''}`; } function borrarTodo(){if(!pue('del'))return;if(!confirm('¿Borrar TODO el historial?'))return;if(!confirm('Segunda confirmación: esto no se puede deshacer.'))return;E.ventas=[];E.gastos=[];E.nVenta=1;logAc('borrar_hist');saveData();notif('🗑️ Historial borrado');renderTab();} // ══════════════════════════════════════ // HISTORIAL // ══════════════════════════════════════ function rHist(){ if(!pue('hist')) return noP(); const hoy=new Date().toDateString(); const vh=E.ventas.filter(v=>new Date(v.ts).toDateString()===hoy).slice().reverse(); const vant=E.ventas.filter(v=>new Date(v.ts).toDateString()!==hoy).slice().reverse(); const vH=v=>{const hr=new Date(v.ts).toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}),is=v.items.map(i=>i.qty+'x '+i.nom).join(', '); return `
🧾
#${String(v.id).padStart(4,'0')} ${METBL[v.met]||v.met}${v._pendingSync?` Pendiente`:''}${v.mesa?` 🪑${v.mesa}`:''}
${hr}${pue('todos')?` · ${san(v.eNom)}`:''}
${is.length>55?is.slice(0,55)+'...':is}
${v.nota?`
📌 ${san(v.nota)}
`:''}
${fU(v.tot)}
${fB(v.totBs)}
${v.disc>0?`
-${fU(v.disc)}
`:''}
📝 Modificaciones del Sistema
${(function(){ var mods=E.bita.filter(function(b){return b.a==='mod';}).slice(0,100); if(!mods.length) return '
Sin modificaciones registradas
'; var TIPO_IC={usuario:'👤',producto:'🍞',config:'⚙️',categoría:'🏷️',sistema:'🔧'}; return mods.map(function(b){ var d=new Date(b.ts); var fecha=d.toLocaleDateString('es-VE',{day:'2-digit',month:'short',year:'numeric'}); var hora=d.toLocaleTimeString('es-VE',{hour:'2-digit',minute:'2-digit'}); var ic=TIPO_IC[b.tipo]||'🔧'; return '
' +'
'+ic+'
' +'
' +'
'+san(b.d)+'
' +'
' +'👤 '+san(b.u)+' · '+''+san(b.tipo)+'' +'
' +'
' +'
' +fecha+'
'+hora +'
' +'
'; }).join(''); })()}
`;}; return `
📋 Historial
${!vh.length&&!vant.length?`
📋
Sin ventas registradas
`:''} ${vh.length?`
📅 Hoy (${vh.length})
${vh.map(vH).join('')}
`:''} ${vant.length?`
📁 Anteriores
${vant.slice(0,40).map(v=>`
${new Date(v.ts).toLocaleDateString('es-VE')}
${vH(v)}`).join('')}
`:''}`; } // ══════════════════════════════════════ // PRODUCTOS // ══════════════════════════════════════ let varArr2=[]; function rProds(){ const canE=pue('eprod'); const cats_cfg=getCats(); const catL={}; cats_cfg.forEach(function(c){catL[c.id]=c.ic+' '+c.lb;}); return `
${canE?'🍞 Gestión de Productos':'📋 Menú'}
${canE?``:''}
${cats_cfg.map(c=>{const cat=c.id;const ps=E.prods.filter(p=>p.cat===cat);if(!ps.length)return'';return `
${catL[cat]}
${ps.map(p=>`
${getProdVisual(p,'list')}
${san(p.nom)}${!p.on?' [OFF]':''}
${fB(toB(p.pr))}${p.vars&&p.vars.length?' · '+p.vars.length+' vars':''}${p.stk!==null&&p.stk!==undefined?' · Stock: '+p.stk:''}
${fU(p.pr)}
${canE?` `:''}
`).join('')}
`;}).join('')}
💡 Precios en dólares

Los precios se guardan en USD. El sistema convierte automáticamente a Bolívares usando la tasa BCV en tiempo real.

`; } function prevProd(){try{const v=safeN(document.getElementById('p-pr').value,0,0);const el=document.getElementById('p-bs');if(v>0){document.getElementById('p-bs-v').textContent=fB(toB(v));el.style.display='block';}else el.style.display='none';}catch(e){}} function rvList(){ const c=document.getElementById('var-list');if(!c)return; c.innerHTML=varArr2.map((v,i)=>`
`).join(''); } function addVarRow(){varArr2.push({lb:'',pr:0});rvList();} function getCats(){ return (E.cfg&&Array.isArray(E.cfg.cats)&&E.cfg.cats.length)?E.cfg.cats:[ {id:'panes',lb:'Panes',ic:'🍞'}, {id:'salados',lb:'Salados',ic:'🥐'}, {id:'dulces',lb:'Dulces',ic:'🧁'}, {id:'bebidas',lb:'Bebidas',ic:'☕'}, {id:'otros',lb:'Otros',ic:'📦'} ]; } function getCatLabel(id){ var c=getCats().find(function(x){return x.id===id;}); return c?(c.ic+' '+c.lb):id; } function buildCatSelect(selectedId){ var cats=getCats(); return cats.map(function(c){ return ''; }).join(''); } function refreshCatSelect(selectedId){ var sel=document.getElementById('p-cat'); if(sel) sel.innerHTML=buildCatSelect(selectedId||'otros'); } function abrProd(){E.editPId=null;varArr2=[];document.getElementById('mprod-t').textContent='➕ Nuevo Producto';['p-n','p-pr','p-pr-bs','p-ic','p-stk'].forEach(id=>document.getElementById(id).value='');refreshCatSelect('panes');document.getElementById('p-on').checked=true;document.getElementById('p-bs').style.display='none';rvList();aM('mo-prod');} function editProd(id){const p=E.prods.find(x=>idEq(x.id,id));if(!p)return;E.editPId=p.id;varArr2=(p.vars||[]).map(v=>({...v}));document.getElementById('mprod-t').textContent='✏️ Editar Producto';document.getElementById('p-n').value=p.nom;document.getElementById('p-pr').value=p.pr;document.getElementById('p-ic').value=p.ic||'';document.getElementById('p-cat').value=p.cat;document.getElementById('p-on').checked=p.on;document.getElementById('p-stk').value=p.stk!==null&&p.stk!==undefined?p.stk:'';rvList();prevProd();aM('mo-prod');} function saveProd(){ try{ const n=document.getElementById('p-n').value.trim(),pr=safeN(document.getElementById('p-pr').value,0,0.01),ic=(document.getElementById('p-ic').value.trim()||'📦').slice(0,2),cat=document.getElementById('p-cat').value,on=document.getElementById('p-on').checked,sv=document.getElementById('p-stk').value; const stk=sv!==''?Math.max(0,Math.floor(parseFloat(sv))):null; if(!n){notif('⚠️ Nombre requerido');return;}if(!pr){notif('⚠️ Precio inválido');return;} const vars=varArr2.filter(v=>v.lb&&v.pr>0); if(E.editPId){const idx=E.prods.findIndex(x=>idEq(x.id,E.editPId));if(idx>=0) E.prods[idx]={...E.prods[idx],nom:san(n),pr,ic,cat,on,stk,vars};notif('✅ Producto actualizado');} else{E.prods.push({id:E.nProd++,nom:san(n),pr,ic,cat,on,stk,vars});notif('✅ Producto agregado');} saveData();cM('mo-prod');renderTab(); }catch(e){notif('❌ Error al guardar producto');} } function delProd(id){const p=E.prods.find(x=>idEq(x.id,id));if(!confirm(`¿Eliminar "${san(p?.nom)}"?`))return;E.prods=E.prods.filter(x=>!idEq(x.id,id));saveData();notif('🗑️ Eliminado');renderTab();} // ══════════════════════════════════════ // USUARIOS // ══════════════════════════════════════ function mountProdEditor(){ try{ const iconInput=document.getElementById('p-ic'); if(iconInput && !iconInput.dataset.boundThumb){ iconInput.dataset.boundThumb='1'; iconInput.addEventListener('input',updateProdThumb); } if(!document.getElementById('p-thumb')){ const stockField=document.getElementById('p-stk')?.closest('.fg'); if(stockField){ const box=document.createElement('div'); box.className='fg'; box.innerHTML=`
📦
Si subes una foto, se mostrara en menu, caja y carrito. Si no, se usa el icono.
`; stockField.parentNode.insertBefore(box,stockField); document.getElementById('p-img-file')?.addEventListener('change',loadProdImage); } } const grid=document.getElementById('p-icon-grid'); if(grid) grid.innerHTML=renderProdIconPalette(); updateProdThumb(); }catch(e){} } abrProd=function(){ E.editPId=null; varArr2=[]; document.getElementById('mprod-t').textContent='Nuevo Producto'; ['p-n','p-pr','p-ic','p-stk'].forEach(id=>document.getElementById(id).value=''); refreshCatSelect('panes'); document.getElementById('p-on').checked=true; document.getElementById('p-bs').style.display='none'; mountProdEditor(); clearProdImage(); rvList(); aM('mo-prod'); }; editProd=function(id){ const p=E.prods.find(x=>idEq(x.id,id));if(!p)return; E.editPId=p.id; varArr2=(p.vars||[]).map(v=>({...v})); document.getElementById('mprod-t').textContent='Editar Producto'; document.getElementById('p-n').value=p.nom; document.getElementById('p-pr').value=p.pr; document.getElementById('p-pr-bs').value=p.prBs||''; document.getElementById('p-ic').value=p.ic||''; document.getElementById('p-cat').value=p.cat; document.getElementById('p-on').checked=p.on; document.getElementById('p-stk').value=p.stk!==null&&p.stk!==undefined?p.stk:''; mountProdEditor(); document.getElementById('p-img-preview').value=p.img||''; updateProdThumb(); rvList(); prevProd(); aM('mo-prod'); }; saveProd=function(){ try{ const n=document.getElementById('p-n').value.trim(); const pr=safeN(document.getElementById('p-pr').value,0,0.01); const prBs=safeN(document.getElementById('p-pr-bs').value,0,0)||null; const ic=(document.getElementById('p-ic').value.trim()||'📦').slice(0,2); const cat=document.getElementById('p-cat').value; const on=document.getElementById('p-on').checked; const sv=document.getElementById('p-stk').value; const img=(document.getElementById('p-img-preview')?.value||'').trim(); const stk=sv!==''?Math.max(0,Math.floor(parseFloat(sv))):null; if(!n){notif('Nombre requerido');return;} if(!pr){notif('Precio invalido');return;} const vars=varArr2.filter(v=>v.lb&&v.pr>0); if(E.editPId){ const idx=E.prods.findIndex(x=>idEq(x.id,E.editPId)); if(idx>=0) E.prods[idx]={...E.prods[idx],nom:san(n),pr,prBs,ic,cat,on,stk,vars,img}; notif('Producto actualizado'); }else{ E.prods.push({id:E.nProd++,nom:san(n),pr,prBs,ic,cat,on,stk,vars,img}); notif('Producto agregado'); } saveData(); cM('mo-prod'); renderTab(); }catch(e){notif('Error al guardar producto');} }; function updPinH(){ try{ const rol=document.getElementById('u-rol').value; const pLen=SEC.LEN[rol]||4; const lbl=document.getElementById('u-pin-lbl'); if(lbl) lbl.textContent=E.editUId?`Nuevo PIN (${pLen} dígitos) — vacío = conservar`:`PIN (${pLen} dígitos) *`; const hint=document.getElementById('u-pin-hint'); if(hint){ const isEditing=!!E.editUId; const curUser=isEditing?E.us.find(x=>x.id===E.editUId):null; const rolChanged=curUser&&curUser.rol!==rol; const oldLen=curUser?SEC.LEN[curUser.rol]||4:0; let msg={superadmin:'Acceso total · 8 dígitos · gestión de usuarios, borrar historial, temas, backup',admin:'Administrador · 6 dígitos · reportes, productos, fiados, clientes, exportar',empleado:'Cajero/Empleado · 4 dígitos · caja, gastos, pedidos, calculadora'}[rol]||''; if(rolChanged&&oldLen!==pLen) msg+=' ⚠️ El rol cambió de longitud — se requiere nuevo PIN'; hint.textContent=msg; } // Limpiar PIN si el rol cambió de longitud if(E.editUId){ const curUser=E.us.find(x=>x.id===E.editUId); if(curUser&&curUser.rol!==rol){ const oldLen=SEC.LEN[curUser.rol]||4; if(oldLen!==pLen) document.getElementById('u-pin').value=''; } } }catch(e){} } function mkUserBtns(u){ var html=''; if(u.rol!=='superadmin'){ html+=' '; } return html; } function rUsers(){ if(!pue('users')) return noP(); return `
🔐 Control de Accesos
${E.us.filter(u=>E.ses&&E.ses.rol==='superadmin'?true:u.rol!=='superadmin').map((u,idx)=>{const col=PALETA[idx%PALETA.length];const init=u.nom.trim().split(/\s+/).map(n=>n[0]).join('').toUpperCase().slice(0,2);const pLen=SEC.LEN[u.rol]||4; return `
${init}
${san(u.nom)} ${u.id===E.ses.id?'':''}
@${san(u.user)} · PIN: ${'●'.repeat(pLen)}
${u.on?'● Activo':'○ Inactivo'}
${pue('users')&&u.id!==E.ses.id?mkUserBtns(u):''} ${u.id!==E.ses.id?``:''}
`;}).join('')}
🔐 Diferencias por Nivel de Acceso
⭐ Acceso Total · 8 dígitos
Todo lo del Admin más: gestión de usuarios, borrar historial, restaurar backup, cambiar tema, editar datos del negocio
🔵 Administrador · 6 dígitos
Ventas, reportes, cierre, historial, productos, gastos, fiados, clientes, pedidos, calculadora, exportar PDF/Excel, tasa y pagos
🟢 Cajero/Empleado · 4 dígitos
Caja (cobrar), gastos, pedidos, calculadora de costos. Solo ve su propio turno
`; } function abrUser(){ E.editUId=null; document.getElementById('mu-t').textContent='👤 Nuevo Usuario'; ['u-nom','u-user','u-pin'].forEach(id=>document.getElementById(id).value=''); document.getElementById('u-pin').placeholder='••••'; document.getElementById('u-rol').value='empleado'; document.getElementById('u-on').checked=true; updPinH();aM('mo-user'); } function editUser(id){ const u=E.us.find(x=>x.id===id);if(!u)return; E.editUId=id; document.getElementById('mu-t').textContent='✏️ Editar usuario'; document.getElementById('u-nom').value=u.nom; document.getElementById('u-user').value=u.user; document.getElementById('u-pin').value=''; // Nunca mostrar PIN actual document.getElementById('u-pin').placeholder='Dejar vacío = conservar PIN actual'; document.getElementById('u-rol').value=u.rol; document.getElementById('u-on').checked=u.on; updPinH(); aM('mo-user'); } function saveUser(){ try{ const nom=document.getElementById('u-nom').value.trim(); const usr=document.getElementById('u-user').value.trim().toLowerCase(); const pin=String(document.getElementById('u-pin').value).replace(/\D/g,''); const rol=document.getElementById('u-rol').value; const on=document.getElementById('u-on').checked; const req=SEC.LEN[rol]||4; if(!pue('users')){notif('⚠️ Sin permiso');return;} if(!nom){notif('⚠️ Nombre requerido');return;} // Solo superadmin puede crear otro superadmin if(rol==='superadmin'&&E.ses.rol!=='superadmin'){notif('⚠️ Sin permiso para crear ese nivel');return;} if(!usr){notif('⚠️ Usuario requerido');return;} if(!/^[a-z0-9_]+$/.test(usr)){notif('⚠️ Usuario: solo letras, números y _');return;} if(E.editUId){ // === EDITAR USUARIO EXISTENTE === const idx=E.us.findIndex(x=>x.id===E.editUId); if(idx<0){notif('❌ Usuario no encontrado');return;} const uu=E.us[idx]; const rolChange=uu.rol!==rol; const oldReq=SEC.LEN[uu.rol]||4; // Si el rol cambia a uno con diferente longitud de PIN, exigir nuevo PIN if(rolChange&&req!==oldReq&&pin.length===0){ notif(`⚠️ El rol cambió: se necesita un PIN nuevo de ${req} dígitos`);return; } if(pin.length>0&&pin.length0&&pin.length>req){notif(`⚠️ PIN demasiado largo: máx ${req} dígitos`);return;} const pinFinal=pin.slice(0,req); // No puede duplicar usuario de otro if(E.us.find(u=>u.user===usr&&u.id!==E.editUId)){notif('⚠️ Ese usuario ya existe');return;} const capturedId=E.editUId; // Capturar antes de cualquier async E.us[idx]={...uu,nom:san(nom),user:san(usr),rol,on}; if(pinFinal){ E.us[idx].pin=pinFinal; delete E.us[idx].pinH; hPin(pinFinal).then(function(h){ const u=E.us.find(function(x){return x.id===capturedId;}); if(u){u.pinH=h;delete u.pin;saveData();} }).catch(function(){}); }else if(rolChange){ // Rol cambia pero misma longitud PIN: el hash sigue siendo válido // No tocar pinH } E.editUId=null; // Resetear después de capturar saveData();cM('mo-user');notif('✅ Usuario actualizado');renderTab(); }else{ // === CREAR NUEVO USUARIO === if(pin.lengthreq){notif(`⚠️ PIN demasiado largo: máx ${req} dígitos`);return;} if(E.us.find(u=>u.user===usr)){notif('⚠️ Ese usuario ya existe');return;} const pinFinal=pin.slice(0,req); const nid=E.nUser++; E.us.push({id:nid,nom:san(nom),user:san(usr),pin:pinFinal,rol,on}); hPin(pinFinal).then(function(h){ const u=E.us.find(function(x){return x.id===nid;}); if(u){u.pinH=h;delete u.pin;saveData();} }).catch(function(){}); E.editUId=null; saveData();cM('mo-user');notif(`✅ Usuario creado · PIN ${req} dígitos`);renderTab(); } }catch(e){notif('❌ Error al guardar usuario: '+san(String(e.message||'')));} } function resetPin(id){ var u=E.us.find(function(x){return x.id===id;}); if(!u) return; var pLen=SEC.LEN[u.rol]||4; var np=prompt('Nuevo PIN para '+u.nom+' ('+pLen+' dígitos exactos):',''); if(np===null||np==='') return; var pinClean=String(np).replace(/\D/g,''); if(pinClean.length!==pLen){notif('PIN debe tener '+pLen+' dígitos');return;} u.pin=pinClean; delete u.pinH; hPin(pinClean).then(function(h){var uu=E.us.find(function(x){return x.id===id;});if(uu){uu.pinH=h;delete uu.pin;}saveData();}).catch(function(){saveData();}); saveData(); notif('PIN de '+san(u.nom)+' cambiado a: '+pinClean); renderTab(); } function delUser(id){ const u=E.us.find(x=>x.id===id); if(!u) return; if(!confirm('¿Eliminar a "'+san(u.nom)+'"?')) return; // Proteger: no borrar al usuario activo if(u.id===E.ses.id){notif('⚠️ No puedes eliminarte a ti mismo');return;} // Proteger: no dejar sin empleados if(u.rol==='empleado'){ const otrosEmp=E.us.filter(x=>x.id!==id&&x.rol==='empleado'&&x.on); if(!otrosEmp.length){notif('⚠️ Debe existir al menos un cajero activo');return;} } E.us=E.us.filter(x=>x.id!==id); saveData();notif('🗑️ Eliminado');renderTab(); } // ══════════════════════════════════════ // CONFIG // ══════════════════════════════════════ function abrNuevaCat(){ var ic=prompt('Ícono (emoji) para la categoría:','🍕'); if(!ic) return; var lb=prompt('Nombre de la categoría:',''); if(!lb||!lb.trim()) return; // Generar ID a partir del nombre var id=lb.trim().toLowerCase() .normalize('NFD').replace(/[\u0300-\u036f]/g,'') .replace(/[^a-z0-9]/g,'_').replace(/_+/g,'_').replace(/^_|_$/g,''); if(!id){notif('⚠️ Nombre inválido');return;} var cats=getCats(); if(cats.find(function(c){return c.id===id;})){ notif('⚠️ Ya existe una categoría con ese ID');return; } cats.push({id:id,lb:lb.trim(),ic:ic.trim()}); E.cfg.cats=cats; saveData(); notif('✅ Categoría "'+lb.trim()+'" creada'); renderTab(); } function editCat(i){ var cats=getCats(); var c=cats[i];if(!c) return; var ic=prompt('Ícono:',c.ic); if(ic===null) return; var lb=prompt('Nombre:',c.lb); if(lb===null) return; if(!lb.trim()){notif('⚠️ Nombre requerido');return;} cats[i]={id:c.id,lb:lb.trim(),ic:ic.trim()||c.ic}; E.cfg.cats=cats; saveData(); notif('✅ Categoría actualizada'); renderTab(); } function delCat(i){ var cats=getCats(); var c=cats[i];if(!c) return; // Verificar si hay productos en esa categoría var prodCount=E.prods.filter(function(p){return p.cat===c.id;}).length; if(prodCount>0){ if(!confirm('La categoría "'+c.lb+'" tiene '+prodCount+' producto(s). ¿Eliminarla igual? Los productos quedarán en "otros".')){return;} E.prods.forEach(function(p){if(p.cat===c.id) p.cat='otros';}); }else{ if(!confirm('¿Eliminar categoría "'+c.lb+'"?')) return; } cats.splice(i,1); E.cfg.cats=cats; saveData(); notif('🗑️ Categoría eliminada'); renderTab(); } // ══════════════════════════════════════ // MEMBRESÍA MUSTAPAN // ══════════════════════════════════════ function membActivo(m){ if(!m||!m.on) return false; var hoy=new Date(); var fin=new Date(m.fin); return hoy<=fin && m.saldo>0; } function membDescuento(){return E.cfg.membDescuento||10;} function rMemb(){ if(!pue('clis')) return noP(); var desc=membDescuento(); var membs=E.memb||[]; var parts=[]; parts.push('
💳 Membresías MUSTAPAN
'); parts.push('
Descuento para miembros activos
'+desc+'%
Configurable en ⚙️ Config
'); if(pue('clis')) parts.push(''); if(!membs.length){ parts.push('
Sin membresías registradas
'); }else{ membs.forEach(function(m,i){ var act=membActivo(m); var fin=new Date(m.fin).toLocaleDateString('es-VE'); var ini=new Date(m.ini).toLocaleDateString('es-VE'); var waTxt=encodeURIComponent('Hola desde MUSTAPAN'); var card=[]; card.push('
'); card.push('
💳
'); card.push('
'+san(m.nom)+'
'); if(m.codigo) card.push('
'+san(m.codigo)+'
'); var telInfo=(m.tel?'📱 '+san(m.tel):'')+(m.tel&&m.telComprador?' · ':''+(m.telComprador?'':''))+(m.telComprador?'🛒 '+san(m.telComprador):''); if(telInfo) card.push('
'+telInfo+'
'); card.push('
'); var aBg=act?'var(--okbg)':'var(--errbg)';var aCol=act?'var(--ok)':'var(--err)'; card.push('
'+(act?'✅ ACTIVO':'❌ INACTIVO')+'
'); card.push('
'); card.push('
'); card.push('
Saldo: $'+m.saldo.toFixed(2)+'
'); card.push('
Pagado: $'+m.montoPagado.toFixed(2)+'
'); card.push('
Desde: '+ini+'
'); card.push('
Hasta: '+fin+'
'); card.push('
'); if(pue('clis')){ card.push('
'); card.push(''); if(m.tel){var n1='58'+m.tel.replace(/\D/g,'').replace(/^0/,'');card.push('📲 Suscriptor');} if(m.telComprador){var n2='58'+m.telComprador.replace(/\D/g,'').replace(/^0/,'');card.push('🛒 Comprador');} card.push(''); var tBg=act?'var(--errbg)':'var(--okbg)';var tCol=act?'var(--err)':'var(--ok)'; card.push(''); card.push('
'); } card.push('
'); parts=parts.concat(card); }); } return parts.join(''); } function abrNuevaMemb(){ // Limpiar form ['mb-nom','mb-tel'].forEach(function(id){var el=document.getElementById(id);if(el)el.value='';}); var mn=document.getElementById('mb-meses');if(mn)mn.value='1'; var mm=document.getElementById('mb-monto');if(mm)mm.value='0'; var pr=document.getElementById('mb-preview');if(pr)pr.style.display='none'; var ne=document.getElementById('mb-nom-err');if(ne)ne.style.display='none'; aM('mo-memb'); // Preview dinámico ['mb-meses','mb-monto'].forEach(function(id){ var el=document.getElementById(id); if(el) el.addEventListener('input',updMembPreview); }); setTimeout(updMembPreview,100); } function updMembPreview(){ var meses=parseInt(document.getElementById('mb-meses')?.value||'1'); var monto=parseFloat(document.getElementById('mb-monto')?.value||'0'); if(isNaN(meses)||meses<1) meses=1; if(isNaN(monto)||monto<0) monto=0; var fin=new Date(); fin.setMonth(fin.getMonth()+meses); var pr=document.getElementById('mb-preview'); var txt=document.getElementById('mb-prev-txt'); if(!pr||!txt) return; var desc=membDescuento(); txt.innerHTML='📅 Válida hasta: '+fin.toLocaleDateString('es-VE',{day:'2-digit',month:'long',year:'numeric'})+'
' +'💵 Saldo inicial: $'+monto.toFixed(2)+'
' +'🏷️ Descuento: '+desc+'% en cada compra'; pr.style.display='block'; } function genCodMemb(){ var chars='ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; var code='MEM-'; for(var i=0;i<6;i++) code+=chars[Math.floor(Math.random()*chars.length)]; return code; } function guardarNuevaMemb(){ var nom=(document.getElementById('mb-nom')?.value||'').trim(); if(!nom){document.getElementById('mb-nom-err').style.display='block';return;} var tel=(document.getElementById('mb-tel')?.value||'').trim(); var tel2=(document.getElementById('mb-tel2')?.value||'').trim(); var meses=parseInt(document.getElementById('mb-meses')?.value||'1'); var monto=parseFloat(document.getElementById('mb-monto')?.value||'0'); if(isNaN(meses)||meses<1) meses=1; if(isNaN(monto)||monto<0) monto=0; var ini=new Date(); var fin=new Date(); fin.setMonth(fin.getMonth()+meses); var codigo=genCodMemb(); var m={id:Date.now(),codigo:codigo,nom:nom,tel:tel,telComprador:'',ini:ini.toISOString(),fin:fin.toISOString(), saldo:monto,montoPagado:monto,consumido:0,on:true,telComprador:tel2}; if(!Array.isArray(E.memb)) E.memb=[]; E.memb.unshift(m); saveData(); logMod('membresía','Nueva: '+nom+' $'+monto.toFixed(2)+' '+meses+' mes'+(meses>1?'es':'')); cM('mo-memb'); notif('✅ Membresía creada — Código: '+codigo); renderTab(); } var _recIdx=null; function recargarMemb(i){ var m=E.memb[i]; if(!m) return; _recIdx=i; var rn=document.getElementById('mo-rec-nom');if(rn)rn.textContent=san(m.nom)+' · Saldo: $'+m.saldo.toFixed(2); var rm=document.getElementById('rec-monto');if(rm)rm.value=''; var rs=document.getElementById('rec-meses');if(rs)rs.value='0'; aM('mo-memb-rec'); } function confirmarRecarga(){ if(_recIdx===null) return; var m=E.memb[_recIdx]; if(!m) return; var monto=parseFloat(document.getElementById('rec-monto')?.value||'0'); if(isNaN(monto)||monto<=0){notif('⚠️ Ingresa un monto válido');return;} var meses=parseInt(document.getElementById('rec-meses')?.value||'0'); m.saldo+=monto; m.montoPagado+=monto; if(meses>0){ var fin=new Date(m.fin); if(fin0?TD.bcv:E.cfg.tasaBCV; return `
⚙️ Configuración
${full?`
🎨 Tema Visual
${Object.keys(TEMAS).map(k=>{const tm=TEMAS[k];return `
${tm.label}
`;}).join('')}
`:''} ${full?`
🏪 Datos del Negocio
`:`

🔒 La configuración avanzada requiere acceso de nivel superior.

`} ${full?`
📱 QR por mesas
Genera codigos QR para que el cliente vea el menu desde su mesa y mande el pedido por WhatsApp. Usa el telefono guardado en Datos del Negocio.
${renderQrMesaLinks(c.qrMesas||12)}
`:''}
💵 Tipo de Cambio
Tasa BCV en tiempo real
$1 = ${fBr(tasaActual)} Bs
${TD.ts||''}
${full?`
Modo manual (fija la tasa, no actualiza auto)
`:''} ${pue('tasa')?``:''}
${full?`
💳 Datos de Pago
Se muestran automáticamente en recibos según el método de pago seleccionado.
`:''} ${full?`
💳 Membresía MUSTAPAN
% de descuento en cada compra
`:''} ${full?`
🏷️ Categorías del Menú
Aquí puedes cambiar los nombres para usar palabras más naturales en Venezuela. Ejemplo: cambiar "Bollería" por "Hojaldres" o "Panes especiales".
${getCats().map((cat,i)=>`
${cat.ic}
${san(cat.lb)}
ID interno: ${san(cat.id)}
`).join('')}
`:''} ${full?`
☁️ Backup

Guarda una copia de seguridad completa de todos los datos.

`:''}`; } function cambTema(t){E.cfg.tema=t;apTema(t);saveData();renderTab();} function saveCfgN(){try{const g=id=>document.getElementById(id)?.value.trim()||'';E.cfg.nom=san(g('cfg-n'))||'MUSTAPAN';E.cfg.slogan=san(g('cfg-sl'));E.cfg.rif=san(g('cfg-rif'));E.cfg.tel=san(g('cfg-tel'));E.cfg.dir=san(g('cfg-dir'));saveData();notif('✅ Datos guardados');renderTab();}catch(e){}} function saveCfgT(){try{const v=safeN(document.getElementById('cfg-tasa').value,0,0),vp=safeN(document.getElementById('cfg-tasap').value,0,0);if(v<100){notif('⚠️ Tasa inválida');return;}E.cfg.tasaBCV=v;if(vp>0) E.cfg.tasaPar=vp;const m=document.getElementById('cfg-manual');if(m) E.cfg.tasaManual=m.checked;if(E.cfg.tasaManual){TD.bcv=v;TD.par=vp||E.cfg.tasaPar;TD.live=false;}saveData();notif('✅ Tasas guardadas');updTasaUI();}catch(e){}} function saveCfgP(){try{const g=id=>document.getElementById(id)?.value.trim()||'';E.cfg.pmBanco=san(g('cfg-pmb'));E.cfg.pmNum=san(g('cfg-pmn'));E.cfg.pmCed=san(g('cfg-pmc'));E.cfg.zelleEmail=san(g('cfg-zelle'));E.cfg.binTag=san(g('cfg-bin'));E.cfg.termica=document.getElementById('cfg-ter')?.value||'58mm';saveData();notif('✅ Datos de pago guardados');renderTab();}catch(e){}} function saveQrMesas(){ try{ const n=Math.min(80,Math.max(1,parseInt(document.getElementById('cfg-qr-mesas')?.value,10)||12)); E.cfg.qrMesas=n; saveData(); notif('Mesas QR guardadas'); renderTab(); }catch(e){notif('No se pudo guardar QR');} } function renderQrMesaLinks(n){ n=Math.min(80,Math.max(1,parseInt(n,10)||12)); let out=''; for(let i=1;i<=n;i++){ const mesa='Mesa '+i; out+=``; } return out; } function printQrMesas(){ try{ const n=Math.min(80,Math.max(1,parseInt(document.getElementById('cfg-qr-mesas')?.value,10)||E.cfg.qrMesas||12)); E.cfg.qrMesas=n; saveData(); let cards=''; for(let i=1;i<=n;i++){ const mesa='Mesa '+i; cards+=`

${mesa}

Escanea para pedir

${san(qrMesaUrl(mesa))}
`; } const w=window.open('','_blank','width=900,height=700'); if(!w){notif('Permite ventanas emergentes para imprimir QR');return;} w.document.write(`QR Mesas MUSTAPAN